// ==++==
//
//   
//    Copyright (c) 2006 Microsoft Corporation.  All rights reserved.
//   
//    The use and distribution terms for this software are contained in the file
//    named license.txt, which can be found in the root of this distribution.
//    By using this software in any fashion, you are agreeing to be bound by the
//    terms of this license.
//   
//    You must not remove this notice, or any other, from this software.
//   
//
// ==--==
namespace System.Globalization {
    using System.Runtime.Remoting;
    using System;
    using System.Text;
    using System.Threading;
    using System.Collections;
    using System.Runtime.InteropServices;
    using System.Runtime.CompilerServices;
    using System.Security;
    using System.Security.Permissions;
    using System.IO;
    using Microsoft.Win32;
    using System.Runtime.Versioning;

    /*=============================================================================
     *
     * Data table for CultureInfo classes.  Used by System.Globalization.CultureInfo.
     *
     ==============================================================================*/
    // Only statics, does not need to be marked with the serializable attribute
    internal class CultureTable : BaseInfoTable {

        // WARNING!
        //
        // This value must be the kept in sync with the field order in the file or everything'll die
        // This is currently copied from culture.h which is generated by nlpgen.pl
        // Currently ILANGUAGE is the first field in the record
        internal const int ILANGUAGE = 0; // Currently we need to know offset into record (in words) of ILANGUAGE to look up records by LCID

        //
        // This is the mask used to check if the flags for GetCultures() is valid.
        //
        private const CultureTypes CultureTypesMask = ~( CultureTypes.NeutralCultures        | CultureTypes.SpecificCultures | 
                                                         CultureTypes.InstalledWin32Cultures | CultureTypes.UserCustomCulture|
                                                         CultureTypes.ReplacementCultures    | CultureTypes.WindowsOnlyCultures |
                                                         CultureTypes.FrameworkCultures);

        // Hashtable for indexing name to get nDataItem.
        private Hashtable hashByName;
        private Hashtable hashByRegionName;
        private Hashtable hashByLcid;

        unsafe private CultureNameOffsetItem* m_pCultureNameIndex;
        unsafe private RegionNameOffsetItem* m_pRegionNameIndex;
        unsafe private IetfNameOffsetItem* m_pIetfNameIndex;
        unsafe private IDOffsetItem* m_pCultureIDIndex;

        private static CultureTable m_defaultInstance;
        
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        static CultureTable()
        {
            m_defaultInstance = new CultureTable("culture.nlp", true);
        }

        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        unsafe internal CultureTable(String fileName, bool fromAssembly) : base(fileName, fromAssembly)
        {
            if (!IsValid)   // intialized in SetDataItemPointers
                return;
                
            hashByName = new Hashtable();
            hashByLcid = new Hashtable();
            hashByRegionName = new Hashtable();

            // Set up index table for finding data item from culture or region name.
            m_pCultureNameIndex = (CultureNameOffsetItem*)(m_pDataFileStart + m_pCultureHeader->cultureNameTableOffset);
            m_pRegionNameIndex = (RegionNameOffsetItem*)(m_pDataFileStart + m_pCultureHeader->regionNameTableOffset);
            m_pIetfNameIndex = (IetfNameOffsetItem*)(m_pDataFileStart + m_pCultureHeader->ietfNameTableOffset);

            // Set up index table for finding data item from LCID.
            m_pCultureIDIndex = (IDOffsetItem*)(m_pDataFileStart + m_pCultureHeader->cultureIDTableOffset);
        }


        // TypeLoadExceptionMessage is going to be used at the time of failing to load CultureTable type and 
        // then m_defaultInstance will be null and we cannot call any NLS+ code. so we cannot call the resource 
        // manager to get any string because resource manager will call culture info code again. So we had to 
        // use non resource string for throwing this exception. 
        // Note, failing to load CUltureTable is rare to happen and we just got it when injecting OOM fault 
        // to the CLR while trying loading some types. 
        internal const string TypeLoadExceptionMessage = "Failure has occurred while loading a type.";

        internal static CultureTable Default {
            get {
                //
                // In case the CultureTable class failed during the type initilaization then we'll have m_defaultInstance = null.
                // to be reliable we throw exception instead of getting access violation.
                //
                
                if (m_defaultInstance == null)
                    throw new TypeLoadException(TypeLoadExceptionMessage);
                
                return (m_defaultInstance);
            }
        }

        ////////////////////////////////////////////////////////////////////////
        //
        // Set Data Item Pointers that are unique to culture table
        //
        ////////////////////////////////////////////////////////////////////////
        internal override unsafe void SetDataItemPointers()
        {
            if (Validate())
            {
                m_itemSize = m_pCultureHeader->sizeCultureItem;
                m_numItem = m_pCultureHeader->numCultureItems;
                m_pDataPool = (ushort*)(m_pDataFileStart + m_pCultureHeader->offsetToDataPool);
                m_pItemData = m_pDataFileStart + m_pCultureHeader->offsetToCultureItemData;
            }
            else
            {
                m_valid = false;
            }
        }

#region CustomCultureValidation

        //
        // ValidateString catch the buffer overrun.
        // Note, offsets and sizes here are in characters not in bytes.
        //
        private unsafe static String CheckAndGetTheString(ushort *pDataPool, uint offsetInPool, int poolSize)
        {
            if (offsetInPool + 2 > poolSize)  // string offset + string length + null termination.
                return null;
                
            char* pCharValues = unchecked((char*)(pDataPool + offsetInPool));
            int stringLength = (int) pCharValues[0];
            if (offsetInPool + stringLength + 2 > poolSize) // string length + string + null termination
                return null;
                
            return new String(pCharValues + 1, 0, stringLength);
        }


        //
        // ValidateString catch the buffer overrun.
        // Note, offsets and sizes here are in characters not in bytes.
        //
        
        private unsafe static bool ValidateString(ushort *pDataPool, uint offsetInPool, int poolSize)
        {
            if (offsetInPool + 2 > poolSize)  // string offset + string length + null termination.
                return false;
                
            char* pCharValues = unchecked((char*)(pDataPool + offsetInPool));
            int stringLength = (int) pCharValues[0];
            if (offsetInPool + stringLength + 2 > poolSize) // string length + string + null termination
                return false;

            return true;
        }

        //
        // ValidateString catch the buffer overrun.
        // Note, offsets and sizes here are in characters not in bytes.
        //
        
        private unsafe static bool ValidateUintArray(ushort *pDataPool, uint offsetInPool, int poolSize)
        {
            if (offsetInPool == 0)
                return true;
            
            if (offsetInPool + 2 > poolSize)  // string offset + string length + null termination.
                return false;

            // Get location of count and make sure its on an odd word
            // (odd words would end in 2 as pointers, we want pCount%4 to be 2 so that
            // when we get past the count our uints are uint aligned)
            ushort* pCount = pDataPool + offsetInPool;
            if (((int)(pCount) & 2) != 2)
                return false;

            int arrayLength = pCount[0];
            if (offsetInPool + (arrayLength * 2) + 2 > poolSize) // array length * 2 (words/dword) + string + null termination
                return false;

            return true;
        }
        
        //
        // ValidateStringArray catch the buffer overrun.
        // Note, offsets and sizes here are in characters not in bytes.
        //
        
        private unsafe static bool ValidateStringArray(ushort *pDataPool, uint offsetInPool, int poolSize)
        {
            if (!ValidateUintArray(pDataPool, offsetInPool, poolSize))
                return false;

            // Get our count        
            ushort* pCount = pDataPool + offsetInPool;
            int arrayLength = pCount[0];

            if (arrayLength == 0)
                return true;

            // Get our dword *
            uint* uints = (uint*)(pCount + 1);

            for (int i=0; i<arrayLength; i++)
            {
                if (!ValidateString(pDataPool, uints[i], poolSize))
                    return false;
            }

            return true;
        }

        //
        // IsValidLcid 
        // Lcid is valid if it is built in lcid or synthetic one. 
        //
        
        private static bool IsValidLcid(int lcid, bool canBeCustomLcid)
        {
            if (canBeCustomLcid && CultureTableRecord.IsCustomCultureId(lcid))
                return true;
            
            if (CultureTable.Default.IsExistingCulture(lcid))
                return true;


            return false;
        }
        
        const uint sizeofNameOffsetItem = 8;  // NameOffsetItem is 8 bytes.

        //
        // Validate
        // this method used to do shallow validation for the custom culture file. it checks for the buffer overrun 
        // also it validate some other fields like parent, lcid, text info, and compare info.
        //
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        internal unsafe bool Validate()
        {
            if (memoryMapFile == null)  // we only validate custom cultures.
                return true;
                
            long fileSize = memoryMapFile.FileSize;

            if (sizeof(EndianessHeader) + sizeof(CultureTableHeader) + sizeof(CultureTableData) + 2 * sizeof(int) > fileSize)
                return false;

            // EndianessHeader
            EndianessHeader* pEndianHeader = (EndianessHeader*) m_pDataFileStart;
#if BIGENDIAN
            if (pEndianHeader->beOffset > fileSize)  
                return false;
#else            
            if (pEndianHeader->leOffset > fileSize)  
                return false;
#endif // BIGENDIAN

            // CultureTableHeader
            if (m_pCultureHeader->offsetToCultureItemData + m_pCultureHeader->sizeCultureItem > fileSize)  { return false; }
            if (m_pCultureHeader->cultureIDTableOffset > fileSize)                                         { return false; }
            if (m_pCultureHeader->cultureNameTableOffset + sizeofNameOffsetItem > fileSize)                { return false; }
            if (m_pCultureHeader->regionNameTableOffset > fileSize)                                        { return false; }
            if (m_pCultureHeader->offsetToCalendarItemData + m_pCultureHeader->sizeCalendarItem > fileSize){ return false; }
            if (m_pCultureHeader->ietfNameTableOffset > fileSize)                                          { return false; }
            if (m_pCultureHeader->offsetToDataPool > fileSize)                                             { return false; }

            ushort *pDataPool = (ushort*)(m_pDataFileStart + m_pCultureHeader->offsetToDataPool);

            int poolSizeInChar = (int) (fileSize - ((long)pDataPool - (long)m_pDataFileStart)) / 2;   // number of characters in the pool

            if (poolSizeInChar <= 0)
                return false; 

            // SNAME
            uint sNameOffset = (uint)((char *)(m_pDataFileStart + m_pCultureHeader->cultureNameTableOffset))[0];

            // CultureTableData
            CultureTableData *cultureData = (CultureTableData*) (m_pDataFileStart + m_pCultureHeader->offsetToCultureItemData);

            if (cultureData->iLanguage == 0x7F || !IsValidLcid(cultureData->iLanguage, true))
                return false;
                
            string cultureName = CheckAndGetTheString(pDataPool, cultureData->sName, poolSizeInChar);
            if (String.IsNullOrEmpty(cultureName))
                return false; 

            if (sNameOffset != cultureData->sName)
            {
                if (!cultureName.Equals(CheckAndGetTheString(pDataPool, sNameOffset, poolSizeInChar)))
                    return false; 
            }
                
            string parentName = CheckAndGetTheString(pDataPool, cultureData->sParent, poolSizeInChar);
            if (parentName == null || parentName.Equals(cultureName, StringComparison.OrdinalIgnoreCase))
                return false; 

            if (!IsValidLcid((int) cultureData->iTextInfo, false) || !IsValidLcid((int) cultureData->iCompareInfo, false))
                return false; 

            // We could use the reflection instead of checking each field manually but this was hurting 
            // the performance (almost 35% from the creation time)

            if (!ValidateString(pDataPool, cultureData->waGrouping, poolSizeInChar))                       { return false; }
            if (!ValidateString(pDataPool, cultureData->waMonetaryGrouping, poolSizeInChar))               { return false; }
            if (!ValidateString(pDataPool, cultureData->sListSeparator, poolSizeInChar))                   { return false; }
            if (!ValidateString(pDataPool, cultureData->sDecimalSeparator, poolSizeInChar))                { return false; }
            if (!ValidateString(pDataPool, cultureData->sThousandSeparator, poolSizeInChar))               { return false; }
            if (!ValidateString(pDataPool, cultureData->sCurrency, poolSizeInChar))                        { return false; }
            if (!ValidateString(pDataPool, cultureData->sMonetaryDecimal, poolSizeInChar))                 { return false; }
            if (!ValidateString(pDataPool, cultureData->sMonetaryThousand, poolSizeInChar))                { return false; }
            if (!ValidateString(pDataPool, cultureData->sPositiveSign, poolSizeInChar))                    { return false; }
            if (!ValidateString(pDataPool, cultureData->sNegativeSign, poolSizeInChar))                    { return false; }
            if (!ValidateString(pDataPool, cultureData->sAM1159, poolSizeInChar))                          { return false; }
            if (!ValidateString(pDataPool, cultureData->sPM2359, poolSizeInChar))                          { return false; }
            if (!ValidateStringArray(pDataPool, cultureData->saNativeDigits, poolSizeInChar))              { return false; }
            if (!ValidateStringArray(pDataPool, cultureData->saTimeFormat, poolSizeInChar))                { return false; }
            if (!ValidateStringArray(pDataPool, cultureData->saShortDate, poolSizeInChar))                 { return false; }
            if (!ValidateStringArray(pDataPool, cultureData->saLongDate, poolSizeInChar))                  { return false; }
            if (!ValidateStringArray(pDataPool, cultureData->saYearMonth, poolSizeInChar))                 { return false; }
            if (!ValidateStringArray(pDataPool, cultureData->saDuration, poolSizeInChar))                  { return false; }
            if (!ValidateString(pDataPool, cultureData->waCalendars, poolSizeInChar))                      { return false; }
            if (!ValidateString(pDataPool, cultureData->sAbbrevLang, poolSizeInChar))                      { return false; }
            if (!ValidateString(pDataPool, cultureData->sISO639Language, poolSizeInChar))                  { return false; }
            if (!ValidateString(pDataPool, cultureData->sEnglishLanguage, poolSizeInChar))                 { return false; }
            if (!ValidateString(pDataPool, cultureData->sNativeLanguage, poolSizeInChar))                  { return false; }
            if (!ValidateString(pDataPool, cultureData->sEnglishCountry, poolSizeInChar))                  { return false; }
            if (!ValidateString(pDataPool, cultureData->sNativeCountry, poolSizeInChar))                   { return false; }
            if (!ValidateString(pDataPool, cultureData->sAbbrevCountry, poolSizeInChar))                   { return false; }
            if (!ValidateString(pDataPool, cultureData->sISO3166CountryName, poolSizeInChar))              { return false; }
            if (!ValidateString(pDataPool, cultureData->sIntlMonetarySymbol, poolSizeInChar))              { return false; }
            if (!ValidateString(pDataPool, cultureData->sEnglishCurrency, poolSizeInChar))                 { return false; }
            if (!ValidateString(pDataPool, cultureData->sNativeCurrency, poolSizeInChar))                  { return false; }
            if (!ValidateString(pDataPool, cultureData->waFontSignature, poolSizeInChar))                  { return false; }
            if (!ValidateString(pDataPool, cultureData->sISO639Language2, poolSizeInChar))                 { return false; }
            if (!ValidateString(pDataPool, cultureData->sISO3166CountryName2, poolSizeInChar))             { return false; }
            if (!ValidateStringArray(pDataPool, cultureData->saDayNames, poolSizeInChar))                  { return false; }
            if (!ValidateStringArray(pDataPool, cultureData->saAbbrevDayNames, poolSizeInChar))            { return false; }
            if (!ValidateStringArray(pDataPool, cultureData->saMonthNames, poolSizeInChar))                { return false; }
            if (!ValidateStringArray(pDataPool, cultureData->saAbbrevMonthNames, poolSizeInChar))          { return false; }
            if (!ValidateStringArray(pDataPool, cultureData->saMonthGenitiveNames, poolSizeInChar))        { return false; }
            if (!ValidateStringArray(pDataPool, cultureData->saAbbrevMonthGenitiveNames, poolSizeInChar))  { return false; }
            if (!ValidateStringArray(pDataPool, cultureData->saNativeCalendarNames, poolSizeInChar))       { return false; }
            if (!ValidateStringArray(pDataPool, cultureData->saAltSortID, poolSizeInChar))                 { return false; }
            if (!ValidateString(pDataPool, cultureData->sEnglishDisplayName, poolSizeInChar))              { return false; }
            if (!ValidateString(pDataPool, cultureData->sNativeDisplayName, poolSizeInChar))               { return false; }
            if (!ValidateString(pDataPool, cultureData->sPercent, poolSizeInChar))                         { return false; }
            if (!ValidateString(pDataPool, cultureData->sNaN, poolSizeInChar))                             { return false; }
            if (!ValidateString(pDataPool, cultureData->sPositiveInfinity, poolSizeInChar))                { return false; }
            if (!ValidateString(pDataPool, cultureData->sNegativeInfinity, poolSizeInChar))                { return false; }
            if (!ValidateString(pDataPool, cultureData->sMonthDay, poolSizeInChar))                        { return false; }
            if (!ValidateString(pDataPool, cultureData->sAdEra, poolSizeInChar))                           { return false; }
            if (!ValidateString(pDataPool, cultureData->sAbbrevAdEra, poolSizeInChar))                     { return false; }
            if (!ValidateString(pDataPool, cultureData->sRegionName, poolSizeInChar))                      { return false; }
            if (!ValidateString(pDataPool, cultureData->sConsoleFallbackName, poolSizeInChar))             { return false; }
            if (!ValidateStringArray(pDataPool, cultureData->saShortTime, poolSizeInChar))                 { return false; }
            if (!ValidateStringArray(pDataPool, cultureData->saSuperShortDayNames, poolSizeInChar))        { return false; }
            if (!ValidateStringArray(pDataPool, cultureData->saDateWords, poolSizeInChar))                 { return false; }
            if (!ValidateString(pDataPool, cultureData->sSpecificCulture, poolSizeInChar))                 { return false; }
            if (!ValidateString(pDataPool, cultureData->sIetfLanguage, poolSizeInChar))                    { return false; }
/*            
            Object cultureTableData = (object) *cultureData;
            Type type = cultureTableData.GetType();
            FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

            for (int i=0; i<fields.Length; i++)
            {
                BCLDebug.Assert(
                        fields[i].Name[0] == 'i' ||
                        fields[i].Name[0] == 's' ||
                        fields[i].Name[0] == 'w' , "Invalid structure field name.");
                
                if (fields[i].Name.Length<2 || (fields[i].Name[0] != 's' && fields[i].Name[0] != 'w'))
                    continue;
                
                Object objectValue = type.InvokeMember(
                                            fields[i].Name, 
                                            BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField, 
                                            null, 
                                            cultureTableData, 
                                            null, 
                                            CultureInfo.InvariantCulture);
                int value;

                if (fields[i].FieldType == typeof(System.UInt16))
                    value = (int) (ushort) objectValue;
                else if (fields[i].FieldType == typeof(System.UInt32))
                    value = (int) (uint) objectValue;
                else
                    return false;

                bool result = false;
                if (fields[i].Name[0] == 's')
                {
                    if (fields[i].Name[1] == 'a')
                        result = ValidateStringArray(value, poolSizeInChar);
                    else
                        result = ValidateString(value, poolSizeInChar);
                }
                else
                {
                    BCLDebug.Assert(fields[i].Name[1] == 'a', "Expected field starts with wa.");
                    result = ValidateString(value, poolSizeInChar);
                }
                
                if (!result)
                    return false;
            }
*/            
            return true;
        }
#endregion CustomCultureValidation


        /*=================================GetDataItemFromName============================
        **Action:   Given a culture name, return a index which points to a data item in
        **          the Culture Data Table
        **Returns:  the data item index. Or -1 if the culture name is invalid.
        **Arguments:
        **      name culture name
        **Exceptions:
        ==============================================================================*/
        unsafe internal int GetDataItemFromCultureName(String name, out int culture, out String actualName) {
            BCLDebug.Assert(name!=null,"CultureTable.GetDataItemFromCultureName(): name!=null");
            culture = -1;
            actualName = "";

            CultureTableItem cti  = (CultureTableItem)hashByName[name];
            if(null != cti &&
               0 != cti.culture) {
                culture = cti.culture;
                actualName = cti.name;
                return (cti.dataItem);
            }

            int left = 0;
            int right = m_pCultureHeader->numCultureNames - 1;

            while (left <= right) {
                int mid = (left+right)/2;

                int result = CompareStringToStringPoolStringBinary(name, m_pCultureNameIndex[mid].strOffset);

                if (result == 0) {
                    cti = new CultureTableItem();
                    int index = cti.dataItem = m_pCultureNameIndex[mid].dataItemIndex;
                    culture = cti.culture = m_pCultureNameIndex[mid].actualCultureID;
                    actualName = cti.name = GetStringPoolString(m_pCultureNameIndex[mid].strOffset);

                    // m_pCultureNameIndex[i].dateItemIndex is the record number for
                    // the information of a culture.
                    // The trick that we play to remove es-ES-Ts is to put a duplicate entry
                    // in the name offset table for es-ES, so that es-ES-Ts is not in the name offset table
                    // Therefore, es-ES-Ts becomes an invalid name.
                    // However, the data item for es-ES-Ts is still there.  It is just that
                    // you can not find it by calling GetDataItemFromName.
                    hashByName[name] = cti;
                    return (index);
                }
                if (result < 0) {
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            }

            culture = -1;
            return (-1);
        }

        //=================================GetDataItemFromRegionName============================
        // Action:   Given a region name, return a index which points to a data item in
        //           the Culture Data Table
        // Returns:  the data item index. Or -1 if the region name is invalid.
        // Arguments:
        //      name region name
        //
        // Note:     The input region name is expected to be lower case, however
        //           the names in the table may be mixed case.  (So its case insensitive
        //           for the table names, but not for the input name which must be lower case)
        // ==============================================================================

        unsafe internal int GetDataItemFromRegionName(String name) {
            BCLDebug.Assert(name!=null,"CultureTable.GetDataItemFromRegionName(): name!=null");

            Object dataItem;
            if ((dataItem = hashByRegionName[name]) != null) {
                return (Int32)dataItem;
            }

            int left = 0;
            int right = m_pCultureHeader->numRegionNames - 1;

            while (left <= right) {
                int mid = (left+right)/2;

                int result = CompareStringToStringPoolStringBinary(name, m_pRegionNameIndex[mid].strOffset);
                if (result == 0) {
                    int index = m_pRegionNameIndex[mid].dataItemIndex;
                    hashByRegionName[name] = index;
                    // m_pRegionNameIndex[i].dateItemIndex is the record number for
                    // the information of a culture.
                    return (index);
                }
                if (result < 0) {
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            }
            return (-1);
        }

        //======================== LookupIetfName ============================
        // Action:   Given an ietf name, get the corresponding culture name.
        //
        // Note:     The input culture name is expected to be lower case, however
        //           the names in the table may be mixed case.  (So its case insensitive
        //           for the table names, but not for the input name which must be lower case)
        //
        // Returns:  The culture name or null if not valid.
        // Arguments:
        //      name culture name
        // Exceptions:
        //==============================================================================
        unsafe internal String LookupIetfName(String name)
        {
            BCLDebug.Assert(name!=null,"CultureTable.LookupIetfName(): name!=null");

            // Get our culture table item, don't have to use cache because it should've
            // been cached already in CultureInfo.GetCultureInfoByIetfLanguageTag().
            int left = 0;
            int right = m_pCultureHeader->numIetfNames - 1;

            while (left <= right)
            {
                int mid = (left+right)/2;
                int result = CompareStringToStringPoolStringBinary(name, m_pIetfNameIndex[mid].strIetfNameOffset);

                if (result == 0)
                {
                    // They matched, return the real culture name
                    return GetStringPoolString(m_pIetfNameIndex[mid].strCultureNameOffset);
                }
                if (result < 0)
                    right = mid - 1;
                else
                    left = mid + 1;
            }

            // Not found, return null
            return null;
        }


        /*=================================GetDataItemFromCultureID============================
        **Action:   Given a culture ID, return a index which points to a data item in
        **          the Culture Data Table
        **Returns:  the data item index.  Or -1 if the culture ID is invalid.
        **Arguments:
        **      cultureID
        **Exceptions: None.
        ==============================================================================*/

        unsafe internal int GetDataItemFromCultureID(int cultureID, out String actualName)
        {
            BCLDebug.Assert(cultureID!=0,"CultureTable.GetDataItemFromCultureID(): cultureID!=0");

            CultureTableItem cti  = (CultureTableItem)hashByLcid[cultureID];
            if(null != cti &&
               0 != cti.culture) {
                actualName = cti.name;
                return (cti.dataItem);
            }

            int left = 0;
            int right = m_pCultureHeader->numCultureNames - 1;

            while (left <= right) {
                int mid = (left+right)/2;
                int result =  cultureID - this.m_pCultureIDIndex[mid].actualCultureID;
                if (result == 0) {
                    cti = new CultureTableItem();
                    int index = cti.dataItem = m_pCultureIDIndex[mid].dataItemIndex;
                    int culture = cti.culture = cultureID;

                    actualName = cti.name = GetStringPoolString(m_pCultureIDIndex[mid].strOffset);

                    hashByLcid[cultureID] = cti;
                    return (index);
                }
                if (result < 0) {
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            }

            actualName = "";
            return (-1);
        }

/*
        internal static bool IsNeutralCulture(int cultureID) {
            //BCLDebug.Assert(cultureID != -1, "-1 cultureID value passed to IsNeutralCulture.");
            if (CultureInfo.GetSubLangID(cultureID) == 0) {
                return (true);
            }
            switch (cultureID) {
                case CultureInfo.zh_CHT_CultureID:
                case CultureInfo.sr_CultureID:
                    return (true);
            }
            return (false);
        }
*/
        internal static bool IsInstalledLCID(int cultureID)
        {
            {
                return CultureInfo.IsValidLCID(cultureID, CultureInfo.LCID_INSTALLED);
            }
        }

        // <SyntheticSupport/>
        // IsExistingCulture is used when caching the name and lcid mapping to detect if it is 
        // a framework culture or synthetic cultures.
        internal unsafe bool IsExistingCulture(int lcid)
        {
            if (lcid == 0) 
                return false;

            string notUsed;
            return (GetDataItemFromCultureID(lcid, out notUsed) >= 0);
        }

        //
        // Enumerate the cultures according the CultureTypes flags:
        //
        // o NeutralCultures
        //      Enumerate neutral custom and framework cultures.
        //
        // o SpecificCultures
        //      Enumerate specific custom and framework cultures. Also it includes the synthetic cultures.
        //
        // o InstalledWin32Cultures
        //      It enumerates the replacement cultures, framework culture, and synthetic cultures that are installed in the OS too.
        //
        // o UserCustomCulture
        //      It enumerates all custom cultures.
        //
        // o ReplacementCultures
        //      It enumerates all replacement custom cultures.
        //
        // o WindowsOnlyCultures
        //      It enumerates all synthetic and replacement synthetic cultures.
        //
        // o FrameworkCultures
        //      It enumerates all framework and replacement framework cultures.
        //
        // o AllCultures
        //      It enumerates all cultures.
        //
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]        
        unsafe internal CultureInfo [] GetCultures(CultureTypes types)
        {
            if ((int)types <= 0 ||  ((int) types & (int)CultureTypesMask) != 0)
            {
                throw new ArgumentOutOfRangeException(
                                "types",
                                String.Format(
                                    CultureInfo.CurrentCulture,
                                    Environment.GetResourceString("ArgumentOutOfRange_Range"), CultureTypes.NeutralCultures, CultureTypes.FrameworkCultures));
            }

            ArrayList cultures = new ArrayList();

            bool isAddSpecific    = ((types & CultureTypes.SpecificCultures)       != 0);
            bool isAddNeutral     = ((types & CultureTypes.NeutralCultures)        != 0);
            bool isAddInstalled   = ((types & CultureTypes.InstalledWin32Cultures) != 0);
            bool isAddUserCustom  = ((types & CultureTypes.UserCustomCulture)      != 0);
            bool isAddReplacement = ((types & CultureTypes.ReplacementCultures)    != 0);
            bool isAddFramework   = ((types & CultureTypes.FrameworkCultures)      != 0);
            bool isAddWindowsOnly = ((types & CultureTypes.WindowsOnlyCultures)    != 0);
            

            if (isAddNeutral || isAddSpecific || isAddFramework || isAddInstalled)
            {
                for (int i = 0; i < m_pCultureHeader->numCultureNames; i++) 
                {
                    int cultureID = this.m_pCultureIDIndex[i].actualCultureID;
                
                    if (CultureInfo.GetSortID(cultureID) != 0 || cultureID == CultureTableRecord.SPANISH_TRADITIONAL_SORT) 
                    {
                        //
                        // This is an alternate sort culture. For now, do nothing. 
                        // Eventually we may add a new CultureTypes flag.
                        //
                    } 
                    else
                    {
                        CultureInfo ci = new CultureInfo(cultureID);
                        CultureTypes ciTypes = ci.CultureTypes;
                        //
                        // Invariant culture (ci.Name.Length = 0) will be returned with the Neutral cultures enumeration.
                        // and will not be returned from specific cultures enumeration. 
                        //
                        
                        if (((ciTypes & CultureTypes.ReplacementCultures) == 0) &&
                            ( isAddFramework ||
                             (isAddSpecific  && ci.Name.Length>0 && ((ciTypes & CultureTypes.SpecificCultures) != 0)) ||
                             (isAddNeutral   && (((ciTypes & CultureTypes.NeutralCultures) != 0) || ci.Name.Length==0)) ||
                             (isAddInstalled && ((ciTypes & CultureTypes.InstalledWin32Cultures) != 0))))

                            cultures.Add(ci); 
                    }
                }
            }


            CultureInfo [] result = new CultureInfo[cultures.Count];
            cultures.CopyTo(result, 0);
            return (result);
        }
    } // CultureTable class 
    
    // internal class for hashing culture table items (LCID/data item)
    // Used since the dataItem's LANGID may not account for the additional
    // sort ID information that may be needed.
    internal class CultureTableItem {
        internal int dataItem;
        internal int culture;
        internal string name;
    }

    /*=============================================================================
    *
    * The native struct of a record in the Culture ID Offset Table.
    * Every instance of this class will be mapped to a memory address in the native side.
    * The memory address is memory mapped from culture.nlp.
    *
    * Every primary language will have its corresponding IDOffset record.  From the data
    * in IDOffset, we can get the index which points to a record in Culture Data Table for
    * a given culture ID.
    *
    * Used by GetDataItem(int cultureID) to retrieve the InternalDataItem for a given
    * culture ID.
    ==============================================================================*/

    [StructLayout(LayoutKind.Sequential)]
    internal struct IDOffsetItem {
        // DWORD: the LCID value to be used with the name
        internal int    actualCultureID;
        // WORD: Index which points to a record in Culture Data Table (InternalDataItem*) for a primary language.
        internal ushort dataItemIndex;
        // WORD: Offset (in words) to a string in the String Pool Table.
        internal ushort strOffset;
    }

    /*=============================================================================
    **
    ** The native struct of a record in the Culture Name Offset Table.
    ** Every instance of this class will be mapped to a memory address in the native side.
    ** The memory address is memory mapped from culture.nlp.
    **
    ** Every culture name will have its corresponding NameOffset record.  From the data
    ** in NameOffset, we can get the index which points to a record in the Cutlure Data Table
    ** for a given culture name.
    **
    ** Used by GetDataItem(String name) to retrieve the InteralDataItem for a given
    ** culture name.
    ==============================================================================*/

    [StructLayout(LayoutKind.Sequential)]
    internal struct CultureNameOffsetItem {
        // WORD: Offset (in words) to a string in the String Pool Table.
        internal ushort strOffset;
        // WORD: Index to a record in Culture Data Table.
        internal ushort dataItemIndex;
        // DWORD: the LCID value to be used with the name
        internal int    actualCultureID;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct RegionNameOffsetItem {
        // WORD: Offset (in words) to a string in the String Pool Table.
        internal ushort strOffset;
        // WORD: Index to a record in Culture Data Table.
        internal ushort dataItemIndex;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct IetfNameOffsetItem {
        // WORD: Offset (in words) to an ietf name string in the String Pool Table.
        internal ushort strIetfNameOffset;
        // WORD: Offset (in words) to a culture name string in the String Pool Table.
        internal ushort strCultureNameOffset;
    }
}
